package pers.cyril.jdb.client.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import pers.cyril.jdb.client.netty.handler.CreateDatabaseResponseHandler;
import pers.cyril.jdb.client.netty.handler.LoginResponseHandler;
import pers.cyril.jdb.client.sender.ConsoleLoginSender;
import pers.cyril.jdb.protocol.codec.PacketCodeCHandler;
import pers.cyril.jdb.protocol.codec.PacketSpliter;
import pers.cyril.jdb.protocol.packet.LoginRequestPacket;

import java.util.concurrent.TimeUnit;

/**
 * 说点什么吧
 *
 * @author Wang Cyril
 * @date 2024/5/20 11:39
 */
public class NettyClient {

    private String host;

    private int port;

    private String username;

    private String password;

    private int maxRetry;

    public NettyClient(String host, int port, int maxRetry) {
        this.host = host;
        this.port = port;
        this.maxRetry = maxRetry;
    }

    public NettyClient(String host, int port, String username, String password, int maxRetry) {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        this.maxRetry = maxRetry;
    }

    public void start() {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .group(workerGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    public void initChannel(NioSocketChannel ch) {
                        ch.pipeline().addLast(new PacketSpliter());
                        ch.pipeline().addLast(PacketCodeCHandler.INSTANCE);
                        ch.pipeline().addLast(LoginResponseHandler.INSTANCE);
                        ch.pipeline().addLast(CreateDatabaseResponseHandler.INSTANCE);
                    }
                });
        connect(bootstrap, workerGroup, maxRetry);

    }

    private void connect(Bootstrap bootstrap, NioEventLoopGroup workerGroup, int retry) {
        // 第几次重试
        int order = maxRetry - retry;
        // 本次重连的间隔
        int delay = 1 << order;
        bootstrap.connect(host, port).addListener(future -> {
            if (future.isSuccess()) {
                System.out.println("连接 " + host + ":" + port + " 成功!");
                Channel channel = ((ChannelFuture) future).channel();
                startLogin(channel);
            } else if (retry > 0) {
                System.err.println("连接 " + host + ":" + port + " 失败，重试次数：" + order);
                bootstrap.config().group().schedule(() -> {
                    connect(bootstrap, workerGroup, retry - 1);
                }, delay, TimeUnit.SECONDS);
            } else {
                System.err.println("连接 " + host + ":" + port + " 失败，重试次数：" + order);
                workerGroup.shutdownGracefully();
            }
        });
    }

    private void startLogin(Channel channel) {
        if(username != null && password != null){
            LoginRequestPacket packet = new LoginRequestPacket();
            packet.setUsername(username);
            packet.setPassword(password);
            channel.writeAndFlush(packet);
        }else {
            ConsoleLoginSender.INSTANCE.send(channel);
        }
    }

}
